{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Writing Python for new Overlay\n",
    "\n",
    "This example will show how to interface to an overlay or hardware library from Python. \n",
    "\n",
    "In this example, we will assume a new overlay has been created with an accelerator that receives data from Python, processes it, and returns the results. \n",
    "\n",
    "A command and data will be sent to the accelerator from Python, the accelerator will process the data, return the results to memory, and acknowledge the transaction has completed.\n",
    "\n",
    "Rather than going through the process or creating a new overlay, for the purposes of this example, the `base` overlay will be used to illustrate the process. The IOP1 memory will be used to act like the accelerator memory, although no processing will be carried out on the data.\n",
    "\n",
    "For this example, we will define the following addresses in the overlay, which are in the IOP1 memory space, and are accessible from Python:\n",
    "\n",
    "|Address                    | Name                | Memory Location |\n",
    "|---------------------------|---------------------|-----------------| \n",
    "|Accelerator base address   | BASE_ADDRESS        | 0x40000000      |  \n",
    "|Accelerator address range  | ADDRESS_LENGTH      | 0x1000          |  \n",
    "|Command Address offset     | CMD_OFFSET          | 0x800           |\n",
    "|Acknowledge Address offset | ACK_OFFSET          | 0x804           |\n",
    "|Input Data Address offset  | INPUT_DATA_OFFSET   | 0x0             |\n",
    "|Output Data Address offset | OUTPUT_DATA_OFFSET  | 0x400           |\n",
    "\n",
    "Assume we only have the following commands for this simple accelerator:\n",
    "\n",
    "|Command | Value| \n",
    "|--------|------| \n",
    "|IDLE    | 0x0  |\n",
    "|PROCESS | 0x1  |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### Create a new Python class\n",
    "The `MMIO` module will be used to read and write to memory, or memory mapped peripherals in the Overlay. As shown in the following example, the steps to instantiate the new class include:\n",
    "    1. `MMIO` is imported.\n",
    "    2. The new class for the accelerator is defined. \n",
    "    3. `MMIO` will be instantiated inside the new module.\n",
    "    \n",
    "Note that a variable, `array_length`, for this module will also be declared. You will see how this is used later.\n",
    "\n",
    "Assume that the accelerator will check the command address when it starts.\n",
    "\n",
    "The Python module must first initialize the command location (`BASE_ADDRESS` + `CMD_OFFSET`) to 0x0 (IDLE). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "```python\n",
    "from pynq import MMIO\n",
    "\n",
    "class my_new_accelerator:\n",
    "    \"\"\"Python class for the PL Acclererator.\n",
    "    \n",
    "    Attributes\n",
    "    ----------\n",
    "    mmio : MMIO\n",
    "        MMIO object that can be read / written between PS and PL.\n",
    "    array_length : int\n",
    "        Length of the array to be processed.\n",
    "       \n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.mmio = MMIO(BASE_ADDRESS,ADDRESS_LENGTH)\n",
    "        self.array_length = 0\n",
    "        self.mmio.write(CMD_OFFSET, 0x0)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### Define the API\n",
    "\n",
    "For this example, we will define two functions: ``load_data()`` and ``process()``. \n",
    "\n",
    "``load_data()`` will write data to the accelerator memory. \n",
    "\n",
    "``process_data()`` will send the start command to the accelerator, wait for an acknowledge, and read back the processed data.\n",
    "\n",
    "* 0x1 will be written to the command location (CMD_OFFSET) from Python.\n",
    "* The accelerator will write 0x1 to the acknowledge location (ACK_OFFSET) when processing is complete.\n",
    "\n",
    "Note how the `array_length` variable is used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "```python\n",
    "def load_data(self, input_data):\n",
    "    self.array_length = len(input_data)\n",
    "    for i in range(self.array_length):\n",
    "        self.mmio.write(INPUT_DATA_OFFSET + i * 4, input_data[i])\n",
    "            \n",
    "def process(self):     \n",
    "    # Send start command to accelerator\n",
    "    self.mmio.write(CMD_OFFSET, 0x1)\n",
    "    output_data = [0] * self.array_length\n",
    "        \n",
    "    # ACK is set to check for 0x0 in the ACK offset\n",
    "    while (self.mmio.read(ACK_OFFSET)) != 0x1:\n",
    "        pass\n",
    "        \n",
    "    # Ack has been received\n",
    "    for i in range(self.array_length):\n",
    "        output_data[i] = self.mmio.read(OUTPUT_DATA_OFFSET + i * 4)\n",
    "            \n",
    "    # Reset Ack\n",
    "    self.mmio.write(ACK_OFFSET, 0x0)      \n",
    "    return output_data\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### Final code\n",
    "The complete code can be found below, and can be executed and tested in this notebook by running the cells below. The code could be copied to a python file, and run directly on the board. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "BASE_ADDRESS          = 0x40000000\n",
    "ADDRESS_LENGTH        = 0x1000\n",
    "CMD_OFFSET            = 0x800\n",
    "ACK_OFFSET            = 0x804\n",
    "INPUT_DATA_OFFSET     = 0x0\n",
    "OUTPUT_DATA_OFFSET    = 0x400\n",
    "        \n",
    "from pynq import MMIO\n",
    "\n",
    "class my_new_accelerator:\n",
    "    \"\"\"Python class for the PL Acclererator.\n",
    "    \n",
    "    Attributes\n",
    "    ----------\n",
    "    mmio : MMIO\n",
    "        MMIO object that can be read / written between PS and PL.\n",
    "    array_length : int\n",
    "        Length of the array to be processed.\n",
    "       \n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.mmio = MMIO(BASE_ADDRESS,ADDRESS_LENGTH)\n",
    "        self.array_length = 0\n",
    "        self.mmio.write(CMD_OFFSET, 0x0)\n",
    "     \n",
    "    def load_data(self, input_data):\n",
    "        self.array_length = len(input_data)\n",
    "        for i in range(self.array_length):\n",
    "            self.mmio.write(INPUT_DATA_OFFSET + i * 4, input_data[i])\n",
    "            \n",
    "    def process(self):     \n",
    "        # Send start command to accelerator\n",
    "        self.mmio.write(CMD_OFFSET, 0x1)\n",
    "        output_data = [0] * self.array_length\n",
    "        \n",
    "        # ACK is set to check for 0x0 in the ACK offset\n",
    "        while (self.mmio.read(ACK_OFFSET)) != 0x1:\n",
    "            pass\n",
    "        \n",
    "        # Ack has been received\n",
    "        for i in range(self.array_length):\n",
    "            output_data[i] = self.mmio.read(OUTPUT_DATA_OFFSET + i * 4)\n",
    "            \n",
    "        # Reset Ack\n",
    "        self.mmio.write(ACK_OFFSET, 0x0)      \n",
    "        return output_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Executing the cell above loads the module into this notebook. This is the equivalent of importing the module (``import my_new_accelerator``) if it was included as part of the pynq package.\n",
    "\n",
    "As explained previously, this notebook does not show you how to create a custom accelerator, however, the python code can be tested with the `base` overlay. In the `base` overlay, the IOP memory (starting at 0x40000000) will be used to simulate writing to an accelerator, and reading back from the accelerator. Notice how the code writes to one area of memory (BASE_ADDRESS + INPUT_DATA_OFFSET), and expects to read back results from another area in memory (BASE_ADDRESS + OUTPUT_DATA_OFFSET). \n",
    "\n",
    "Execute the cell below to load the overlay, instantiate the accelerator, and send some data to the accelerator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Data to be sent to the accelerator: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n"
     ]
    }
   ],
   "source": [
    "from pynq import Overlay\n",
    "Overlay(\"base.bit\").download()\n",
    "\n",
    "# declare accelerator with an array length of 10\n",
    "acc = my_new_accelerator()\n",
    "input_data = [i for i in range(10)]\n",
    "print(\"Data to be sent to the accelerator:\", input_data)\n",
    "acc.load_data(input_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "As the accelerator doesn't exist, any data loaded to memory won't be processed, and the acknowledge will not be written. \n",
    "\n",
    "Execute the cell below to use the ``MMIO`` to manually write some data to the results area of the memory to simulate data being processed, and to write 0x1 to the acknowledge address. \n",
    "\n",
    "The ``MMIO`` can be very useful to peak and poke memory and memory mapped peripherals in the overlay to debug Python code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "from pynq import MMIO\n",
    "       \n",
    "mmio = MMIO(BASE_ADDRESS, ADDRESS_LENGTH)\n",
    "\n",
    "for i in range(len(input_data)):\n",
    "    mmio.write(OUTPUT_DATA_OFFSET + i * 4, input_data[i] + 1)\n",
    "\n",
    "mmio.write(ACK_OFFSET, 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "The ``process()`` function can now send a start command, read the acknowledge (which has already been set manually in the cell above), and read back from data from the processed data area. You can change the code above to write different data to the processed data area, or to set the acknowlege to 0 (which will cause the code below to hang)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input Data   :  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
      "Output Data  :  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n"
     ]
    }
   ],
   "source": [
    "output_data = acc.process()\n",
    "print(\"Input Data   : \", input_data)\n",
    "print(\"Output Data  : \", output_data)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
